package beer.zivelin.awesome.walawala.mobile.service;

import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.util.Log;

import com.google.common.base.Stopwatch;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

import beer.zivelin.awesome.walawala.mobile.dao.ApplicationStore;
import beer.zivelin.awesome.walawala.mobile.entity.ApplicationData;
import beer.zivelin.awesome.walawala.mobile.entity.ApplicationDataPO;

public class ApplicationLocator {
    public static final String PKG_SELF = "beer.zivelin.awesome.walawala.mobile";
    private static final String TAG = "ApplicationLocator";
    public static String LIKE_MODE_PREFIX = "?";
    private static String sqlLikeChar = "%";

    protected Activity activity;
    protected ApplicationStore store;

    public ApplicationLocator(Activity activity) {
        this.activity = activity;
        this.store = new ApplicationStore(activity);
    }

    public List<ApplicationData> match(String t9Spell) {
        Stopwatch stopwatch = Stopwatch.createStarted();
        if(isLikeMode(t9Spell)){
            t9Spell = generateT9SpellAsLikeMode(t9Spell);
        }
        List<ApplicationData> findoutList = store.searchByT9(t9Spell);
        Log.w(TAG, "match t9 used " + stopwatch.elapsed(TimeUnit.MILLISECONDS) + " ms");
        return findoutList;
    }

    boolean isLikeMode(String t9Spell) {
        return t9Spell.startsWith(ApplicationLocator.LIKE_MODE_PREFIX);
    }

    String generateT9SpellAsLikeMode(String t9Spell) {
        StringBuilder likeSpellBuffer = new StringBuilder();
        boolean duplicatedLikePrefix = false;
        for (char c : t9Spell.substring(1).toCharArray()) {
            if (ApplicationLocator.LIKE_MODE_PREFIX.equals(String.valueOf(c))) {
                if (!duplicatedLikePrefix) {
                    likeSpellBuffer.append(sqlLikeChar);
                    duplicatedLikePrefix = true;
                } else {
                    //skip
                }
            } else {
                duplicatedLikePrefix = false;
                likeSpellBuffer.append(c);
            }
        }
        t9Spell = likeSpellBuffer.toString();
        if (t9Spell.startsWith(sqlLikeChar)) {
            t9Spell = t9Spell.substring(1);
        }
        if (t9Spell.endsWith(sqlLikeChar)) {
            t9Spell = t9Spell.substring(0, t9Spell.length() - 1);
        }
        return t9Spell;
    }

    public void open() {
        store.open();
    }

    public void close() {
        store.close();
    }

    public void loadApplications(Consumer<ApplicationLocator> callback) {
//        if (store.isNewer()) {
        refreshApplications(callback);
//        } else {
//            if (callback != null) {
//                callback.accept(this);
//            }
//        }
    }


    public void refreshApplications(Consumer<ApplicationLocator> callback) {
        Stopwatch stopwatch = Stopwatch.createStarted();
        Log.d(TAG, "start refresh");

        PackageManager pm = activity.getPackageManager();
        Intent query = new Intent();
        query.setAction(Intent.ACTION_MAIN);
        query.addCategory(Intent.CATEGORY_LAUNCHER);

        List<ResolveInfo> applications = pm.queryIntentActivities(query, PackageManager.MATCH_ALL);
        Log.d(TAG, "finish get package, count: " + applications.size() + ", used: " + stopwatch.elapsed(TimeUnit.MILLISECONDS) + " ms");

        new Thread(() -> {
            List<ApplicationData> applicationDataList = new LinkedList<>();
            for (ResolveInfo resolveInfo : applications) {
                String packageName = resolveInfo.activityInfo.packageName;
                if (packageName.equals(PKG_SELF)) {
                    continue;
                }
                ApplicationData app = new ApplicationData();
                app.setPackageName(packageName);
                CharSequence label = resolveInfo.loadLabel(pm);
                app.setName(label == null ? "未知" : label.toString());
                store.loadIcon(app);
                applicationDataList.add(app);
            }

            batchImportApplication(applicationDataList);
            if (callback != null) {
                callback.accept(ApplicationLocator.this);
            }
        }).start();
        Log.d(TAG, "finish refresh, used: " + stopwatch.elapsed(TimeUnit.MILLISECONDS) + " ms");
    }

    protected void batchImportApplication(List<ApplicationData> applicationDataList) {
        Set<String> installedPackages = new HashSet<>();
        applicationDataList.forEach(data -> {
            installedPackages.add(data.getPackageName());
        });

        Set<String> savedPackages = store.selectPackages();

        Set<String> toInsert = findAdded(savedPackages, installedPackages);
        List<ApplicationDataPO> toInsertPOList = new LinkedList<>();
        applicationDataList.forEach(data -> {
            if (toInsert.contains(data.getPackageName())) {
                toInsertPOList.addAll(addAsT9(data));
            }
        });

        Set<String> toDelete = findDeleted(savedPackages, installedPackages);


        store.batchImport(toInsertPOList, toDelete);

    }

    protected Set<String> getSavedPackages() {
        return store.selectPackages();
    }

    protected Set<String> findDeleted(Set<String> savedPackage, Set<String> packages) {
        Set<String> temp = new HashSet<>();
        temp.addAll(savedPackage);
        temp.removeAll(packages);
        return temp;
    }

    protected Set<String> findAdded(Set<String> savedPackage, Set<String> packages) {
        Set<String> temp = new HashSet<>();
        temp.addAll(packages);
        temp.removeAll(savedPackage);
        return temp;
    }

    protected void clearAll() {
        this.store.deleteAll();
    }

    protected List<ApplicationDataPO> addAsT9(ApplicationData app) {
        List<ApplicationDataPO> dataList = new LinkedList<>();
        Set<String> t9List = T9.getInstance().toT9BySentence(app.getName());
        t9List.forEach(t9 -> {
            ApplicationData data = new ApplicationData();
            data.setName(app.getName());
            data.setPackageName(app.getPackageName());
            data.setIcon(app.getIcon());

            ApplicationDataPO po = new ApplicationDataPO(data, t9, ApplicationDataPO.OperType.ADD);
            dataList.add(po);
        });
        return dataList;
    }
}
